/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997, 1998, 1999  Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Sam Lantinga
    slouken@devolution.com
*/

#include <stdio.h>

#include <SDL/SDL.h>
#include "SDL_blit.h"

#ifndef LIMITED_LINKER

/* Functions to perform alpha blended blitting */

static void BlitBtoNAlpha(int width, int height,
			Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
			Uint8 *dst, SDL_PixelFormat *dstfmt, int dstskip)
{
	const SDL_Color *srcpal	= srcfmt->palette->colors;
	Uint8  dstbpp;
	Uint32 pixel;
	int c;
	Uint8  byte, bit;
	Uint8  sR, sG, sB;
	Uint8  dR, dG, dB;
	const Uint8 A = srcfmt->alpha;

	/* Set up some basic variables */
	dstbpp = dstfmt->BytesPerPixel;
	srcskip += width-(width+7)/8;

	while ( height-- ) {
	    	for ( c=0; c<width; ++c ) {
			if ( (c%8) == 0 ) {
				byte = *src++;
			}
			bit = (byte&0x80)>>7;
			if ( 1 ) {
				sR = srcpal[bit].r;
				sG = srcpal[bit].g;
				sB = srcpal[bit].b;
				DISEMBLE_RGB(dst, dstbpp, dstfmt,
							pixel, dR, dG, dB);
				ALPHA_BLEND(sR, sG, sB, A, 255, dR, dG, dB);
			  	ASSEMBLE_RGB(dst, dstbpp, dstfmt, dR, dG, dB);
			}
			byte <<= 1;
			dst += dstbpp;
		}
		src += srcskip;
		dst += dstskip;
	}
}
static void Blit1toNAlpha(int width, int height,
			Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
			Uint8 *dst, SDL_PixelFormat *dstfmt, int dstskip)
{
	const SDL_Color *srcpal	= srcfmt->palette->colors;
	Uint8  dstbpp;
	Uint32 pixel;
	int c;
	Uint8  sR, sG, sB;
	Uint8  dR, dG, dB;
	const Uint8 A = srcfmt->alpha;

	/* Set up some basic variables */
	dstbpp = dstfmt->BytesPerPixel;

	while ( height-- ) {
	    	for ( c=width; c; --c ) {
			if ( 1 ) {
				sR = srcpal[*src].r;
				sG = srcpal[*src].g;
				sB = srcpal[*src].b;
				DISEMBLE_RGB(dst, dstbpp, dstfmt,
							pixel, dR, dG, dB);
				ALPHA_BLEND(sR, sG, sB, A, 255, dR, dG, dB);
			  	ASSEMBLE_RGB(dst, dstbpp, dstfmt, dR, dG, dB);
			}
			src++;
			dst += dstbpp;
		}
		src += srcskip;
		dst += dstskip;
	}
}
static void BlitNto1Alpha(int width, int height,
			Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
			Uint8 *palmap,
			Uint8 *dst, SDL_PixelFormat *dstfmt, int dstskip)
{
	Uint8  srcbpp;
	Uint32 pixel;
	int c;
	Uint8  sR, sG, sB;
	Uint8  dR, dG, dB;

	/* Set up some basic variables */
	srcbpp = srcfmt->BytesPerPixel;

	if ( srcfmt->alpha ) {
	    const Uint8 A = srcfmt->alpha;

	    while ( height-- ) {
		for ( c=width; c; --c ) {
			DISEMBLE_RGB(src, srcbpp, srcfmt, pixel, sR, sG, sB);
			if ( 1 ) {
				dR = dstfmt->palette->colors[*dst].r;
				dG = dstfmt->palette->colors[*dst].g;
				dB = dstfmt->palette->colors[*dst].b;
				ALPHA_BLEND(sR, sG, sB, A, 255, dR, dG, dB);
				/* Pack RGB into 8bit pixel */
				if ( palmap == NULL ) {
					*dst =((dR>>5)<<(3+2))|
					      ((dG>>5)<<(2))|
					      ((dB>>6)<<(0));
				} else {
					*dst = palmap[((dR>>5)<<(3+2))|
						      ((dG>>5)<<(2))  |
						      ((dB>>6)<<(0))  ];
				}
			}
			dst++;
			src += srcbpp;
		}
		src += srcskip;
		dst += dstskip;
	    }
	} else {
	    Uint32 pixel;
	    Uint8  sA;
	    const Uint8  Adiv = (srcfmt->Amask>>srcfmt->Ashift);

	    while ( height-- ) {
		for ( c=width; c; --c ) {
			DISEMBLE_RGBA(src,srcbpp,srcfmt,pixel,sR,sG,sB,sA);
			if ( 1 ) {
				dR = dstfmt->palette->colors[*dst].r;
				dG = dstfmt->palette->colors[*dst].g;
				dB = dstfmt->palette->colors[*dst].b;
				ALPHA_BLEND(sR, sG, sB, sA, Adiv, dR, dG, dB);
				/* Pack RGB into 8bit pixel */
				if ( palmap == NULL ) {
					*dst =((dR>>5)<<(3+2))|
					      ((dG>>5)<<(2))|
					      ((dB>>6)<<(0));
				} else {
					*dst = palmap[((dR>>5)<<(3+2))|
						      ((dG>>5)<<(2))  |
						      ((dB>>6)<<(0))  ];
				}
			}
			dst++;
			src += srcbpp;
		}
		src += srcskip;
		dst += dstskip;
	    }
	}
}
static void Blit4to2Alpha(int width, int height,
		Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
		Uint8 *dst, SDL_PixelFormat *dstfmt, int dstskip)
{
	Uint32 *srcp;
	Uint16 *dstp;
	Uint16 pixel16;
	Uint32 pixel32;
	int c;
	Uint8  sR, sG, sB;
	Uint8  dR, dG, dB;

	/* Set up some basic variables */
	srcp = (Uint32 *)src;
	dstp = (Uint16 *)dst;
	srcskip /= 4;
	dstskip /= 2;

	if ( srcfmt->alpha ) {
	    const Uint8 A = srcfmt->alpha;

	    while ( height-- ) {
		for ( c=width; c; --c ) {
			pixel32 = *srcp;
			RGB_FROM_PIXEL(pixel32, srcfmt, sR, sG, sB);
			if ( 1 ) {
				pixel16 = *dstp;
				RGB_FROM_PIXEL(pixel16, dstfmt, dR, dG, dB);
				ALPHA_BLEND(sR, sG, sB, A, 255, dR, dG, dB);
				PIXEL_FROM_RGB(*dstp, dstfmt, dR, dG, dB);
			}
			++srcp;
			++dstp;
		}
		srcp += srcskip;
		dstp += dstskip;
	    }
	} else {
	    Uint8  sA;
	    const Uint8  Adiv = (srcfmt->Amask>>srcfmt->Ashift);

	    while ( height-- ) {
		for ( c=width; c; --c ) {
			pixel32 = *srcp;
			RGBA_FROM_PIXEL(pixel32, srcfmt, sR, sG, sB, sA);
			if ( 1 ) {
				pixel16 = *dstp;
				RGB_FROM_PIXEL(pixel16, dstfmt, dR, dG, dB);
				ALPHA_BLEND(sR, sG, sB, sA, Adiv, dR, dG, dB);
				PIXEL_FROM_RGB(*dstp, dstfmt, dR, dG, dB);
			}
			++srcp;
			++dstp;
		}
		srcp += srcskip;
		dstp += dstskip;
	    }
	}
}
static void BlitNtoNAlpha(int width, int height,
		Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
		Uint8 *dst, SDL_PixelFormat *dstfmt, int dstskip)
{
	Uint8  srcbpp;
	Uint8  dstbpp;
	Uint32 pixel;
	int c;
	Uint8  sR, sG, sB;
	Uint8  dR, dG, dB;

	/* Set up some basic variables */
	srcbpp = srcfmt->BytesPerPixel;
	dstbpp = dstfmt->BytesPerPixel;

	if ( srcfmt->alpha ) {
	    const Uint8 A = srcfmt->alpha;

	    while ( height-- ) {
		for ( c=width; c; --c ) {
			DISEMBLE_RGB(src, srcbpp, srcfmt, pixel, sR, sG, sB);
			if ( 1 ) {
				DISEMBLE_RGB(dst, dstbpp, dstfmt,
							pixel, dR, dG, dB);
				ALPHA_BLEND(sR, sG, sB, A, 255, dR, dG, dB);
				ASSEMBLE_RGB(dst, dstbpp, dstfmt, dR, dG, dB);
			}
			src += srcbpp;
			dst += dstbpp;
		}
		src += srcskip;
		dst += dstskip;
	    }
	} else {
	    Uint8  sA;
	    const Uint8  Adiv = (srcfmt->Amask>>srcfmt->Ashift);

	    while ( height-- ) {
		for ( c=width; c; --c ) {
			DISEMBLE_RGBA(src,srcbpp,srcfmt,pixel,sR,sG,sB,sA);
			if ( 1 ) {
				DISEMBLE_RGB(dst, dstbpp, dstfmt,
							pixel, dR, dG, dB);
				ALPHA_BLEND(sR, sG, sB, sA, Adiv, dR, dG, dB);
				ASSEMBLE_RGB(dst, dstbpp, dstfmt, dR, dG, dB);
			}
			src += srcbpp;
			dst += dstbpp;
		}
		src += srcskip;
		dst += dstskip;
	    }
	}
}

#endif /* LIMITED_LINKER */

void SDL_BlitAlpha(SDL_BlitInfo *info)
{
#ifdef LIMITED_LINKER
	fprintf(stderr, "You need the full MetroWerks linker for this blit\n");
#else
	Uint8 *src, *palmap, *dst;
	int width, height;
	int srcskip, dstskip;
	SDL_PixelFormat *srcfmt, *dstfmt;

	width = info->d_width;
	height = info->d_height;
	src = info->s_pixels;
	srcskip = info->s_skip;
	srcfmt = info->src;
	dst = info->d_pixels;
	dstskip = info->d_skip;
	dstfmt = info->dst;
	dst = info->d_pixels;
	palmap = info->table;

	switch (srcfmt->BytesPerPixel) {
	case 1:
		switch (dstfmt->BytesPerPixel) {
		case 1:
		  /* We don't implement a 1-1 alpha blit */
		  break;
		case 2:
		case 3:
		case 4:
		  if ( srcfmt->BitsPerPixel == 8 ) {
		  	Blit1toNAlpha(width, height, src, srcfmt, srcskip,
							dst, dstfmt, dstskip);
		  } else
		  if ( srcfmt->BitsPerPixel == 1 ) {
		  	BlitBtoNAlpha(width, height, src, srcfmt, srcskip,
							dst, dstfmt, dstskip);
		  } else {
			/* FIXME:  Unsupported bit-depth */;
		  }
		  break;
		default:
		  break;
		}
	  break;
	case 2:
	case 3:
		switch (dstfmt->BytesPerPixel) {
		case 1:
		  BlitNto1Alpha(width, height, src, srcfmt, srcskip,
						palmap, dst, dstfmt, dstskip);
		  break;
		case 2:
		case 3:
		case 4:
		  BlitNtoNAlpha(width, height, src, srcfmt, srcskip,
							dst, dstfmt, dstskip);
		  break;
		}
	  break;
	case 4:
		switch (dstfmt->BytesPerPixel) {
		case 1:
		  BlitNto1Alpha(width, height, src, srcfmt, srcskip,
						palmap, dst, dstfmt, dstskip);
		  break;
		case 2:
		  Blit4to2Alpha(width, height, src, srcfmt, srcskip, 
							dst, dstfmt, dstskip);
		  break;
		case 3:
		case 4:
		  BlitNtoNAlpha(width, height, src, srcfmt, srcskip, 
							dst, dstfmt, dstskip);
		  break;
		}
	  break;
	}
#endif /* LIMITED_LINKER */
}
